<template>
  <div class="sea-anchor-point">
    <div ref="domRef" v-show="props.modelValue" style="position: absolute;">
      <slot></slot>
    </div>
  </div>
</template>

<script setup lang="ts">
import {onMounted, onUnmounted, ref, watch} from "vue";
import Rect, {Align, Direction} from "@/lang/popover/rect";
import Popover from "@/lang/popover";

/**
 * 气泡弹窗
 *
 * 原理：
 *
 * 在界面中，增加定位节点（sea-anchor-point），
 * 计算按钮与定位节点之间的位置差，推导出气泡窗口的位置信息。
 * 底层原理，还是相对布局，只需要保证 “弹窗、按钮和定位节点” 三者距离一定，就能保证界面不会错位。
 *
 * 注意：
 *
 * 当前版本，没有气泡角标设计，需要采用何种角标，直接在 slot 中添加即可。
 */
interface Props {
    /** model-value 是否打开窗口 */
    modelValue: boolean
    /** 需要吸附的组件  */
    reference?: HTMLElement
    /** 相对于吸附组件的对齐方式 */
    alignAt?: Align
    /** 相对于吸附组件的方向 */
    direction?: Direction
    /** 偏移量，组件之间的留白间隙 */
    offset?: string | number
}

// 使用 withDefaults 设置默认值
const props = withDefaults(defineProps<Props>(), {
    modelValue: false,
    alignAt: 'center',
    direction: 'bottom',
    offset: 0
})

// Emits
const emit = defineEmits(['update:modelValue', 'change']);

// 切换了引用的组件
watch(() => props.reference, (val) => {
    if (props.modelValue === true) {
        open(val);
    }
})

// 气泡窗口的 dom 对象
// 实际上是父级，用于控制界面的显示隐藏，可以把这一层级优化掉
const domRef = ref();

// 气泡 矩形定义
const dom_rect = new Rect();
// 按钮 矩形定义
const ref_rect = new Rect();
// 锚点 矩形定义
const anchor_rect = new Rect();

/**
 * 打开弹窗
 * @param reference - 定位组件
 */
const open = (reference: HTMLElement) => {
    if (reference !== undefined) {

        const dom: HTMLElement = domRef.value;

        dom_rect.ofDOM(dom);
        ref_rect.ofDOM(reference)
        anchor_rect.ofDOM(dom.parentElement);

        // 计算按钮的相对位置
        let left = ref_rect.left - anchor_rect.left;
        let top = ref_rect.top - anchor_rect.top;
        ref_rect.setLocation([left, top]);

        // 计算气泡窗口的相对位置
        const location = Popover.openOf(dom_rect, ref_rect, props.direction, props.alignAt, Number(props.offset));

        // 遍历所有子节点
        domRef.value.style.left = location[0] + 'px';
        domRef.value.style.top = location[1] + 'px';
    }
}

// 如果有必要，可以增加元素不可见时的降级处理
// const observer = new IntersectionObserver((entries) => {
//     entries.forEach(entry => {
//         if (entry.intersectionRatio === 0) {
//             console.log('元素被完全遮盖');
//         } else {
//             console.log('元素可见，可见比例:', entry.intersectionRatio);
//         }
//     });
// }, { threshold: [0, 0.1, 0.5, 1.0] }); // 设置多个阈值
//
// observer.observe(dom);

/**
 * 窗口点击事件，除了触发组件和弹窗组件，任何点击事件都要关闭窗口
 *
 * @param evt - 事件
 */
const windowClick = (evt) => {
    if (props.modelValue === true) {
        const target = evt.target;
        if (!domRef.value.contains(target) && !props.reference.contains(target)) {
            emit('update:modelValue', false);
        }
    }
}

onMounted(() => {
    window.addEventListener('click', windowClick);
})

onUnmounted(() => {
    window.removeEventListener('click', windowClick);
})
</script>
